/*
 * Copyright 2011 Matthew Jannace
 *
 * This file is part of RoboCode BlackHoleAgent
 *
 * RoboCode BlackHoleAgent is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * RoboCode BlackHoleAgent is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RoboCode BlackHoleAgent. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package com.jannaceltd.TeamBlackHoleBeer.Modules.Strategy;

import com.jannaceltd.TeamBlackHoleBeer.BlackHole_Abstract;
import com.jannaceltd.TeamBlackHoleBeer.BlackHole_Interface;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.Enemy;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.Robots;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.TeamMate;
import com.jannaceltd.TeamBlackHoleBeer.Modules.HelperClasses.VirtualBullets;
import com.jannaceltd.TeamBlackHoleBeer.Utilities.MathUtils;
import com.jannaceltd.TeamBlackHoleBeer.Utilities.PolygonUtilities;
import java.awt.geom.Point2D;
import robocode.util.Utils;

/**
 * Class Performs all Team Strategy Actions
 * @author Matthew Jannace <mjannace@gmail.com>
 * @version 1.0
 */
public class TeamStrategy implements BlackHole_Interface {

    private BlackHole_Abstract Agent;
    private boolean NonTeamTargetPursuit;
    private long LastTime_FindCenteroid = -1;
    private long LastTime_ChooseTarget = -1;
    private long LastTime_ClosestObjectToAgent = -1;
    private long LastTime_amIClosestToTarget = -1;
    private double closestObjectToAgent = 0d;
    private boolean amIClosestToTarget = true;

    /**
     * Creates a new TeamStrategy Object
     * @param TeamAgent
     */
    public TeamStrategy(BlackHole_Abstract TeamAgent) {
        this.Agent = TeamAgent;
    }

    /**
     * Finds the Centeroid (Center Of Mass) of the Team Agents
     */
    public void FindCenteroid() {
        if (LastTime_FindCenteroid < Agent.getTime()) {
            LastTime_FindCenteroid = Agent.getTime();
            if ((Agent.teamates.size() + 1) <= 2) {
                FindCenteroid_2Bots();
            } else {
                FindCenteroid_Polygon();
            }
        }
    }

    /**
     * Finds the Centeroid (Center Of Mass) of two Agents in the Team
     */
    public void FindCenteroid_2Bots() {
        Point2D.Double center = new Point2D.Double(Agent.getX(), Agent.getY());
        double distance = 0;
        double angle = 0;
        if (Agent.teamates.size() + 1 >= 2) {
            TeamMate tm[] = new TeamMate[2];
            (Agent.teamates.values()).toArray(tm);
            distance = (Agent.getPos().distance(tm[0].getPos())) / 2;
            angle = Utils.normalAbsoluteAngle(MathUtils.calcAngle(tm[0].getPos(), Agent.getPos()));
            center = MathUtils.project(Agent.getPos(), angle, distance);
        }
        Agent.setCenterMass(center);
    }

    /**
     * Finds the Centeroid (Center Of Mass) of 3+ Agents in the Team
     */
    public void FindCenteroid_Polygon() {
        Point2D[] teamMateLocations = new Point2D[Agent.teamates.size() + 1];
        int i = 0;
        teamMateLocations[i] = new Point2D.Double(Agent.getX(), Agent.getY());

        for (TeamMate tm : Agent.teamates.values()) {
            i++;
            if (tm.getAlive() == true) {
                teamMateLocations[i] = new Point2D.Double(tm.getX(), tm.getY());
            }
        }
        Agent.setCenterMass((Point2D.Double) PolygonUtilities.centerOfMass(teamMateLocations));
    }

    /**
     * Finds the Closest Target to Team
     * @return Enemy
     */
    public Enemy ClosestTargetToTeam() {
        Enemy ClosestTarget = new Enemy();
        FindCenteroid();

        double LastDistance = Agent.getBattleFieldHeight() * Agent.getBattleFieldWidth();

        for (Enemy en : Agent.enemies.values()) {
            if (en.getAlive()) {
                double distance = Agent.getCenterMass().distance(en.getPos());

                if (distance < LastDistance) {
                    LastDistance = distance;
                    ClosestTarget = en;
                }
            }
        }
        return ClosestTarget;
    }

    /**
     * Chooses target that is the Closest Target to Team and sets them as the target
     */
    public void ChooseTarget() {
        if (LastTime_ChooseTarget < Agent.getTime()) {
            LastTime_ChooseTarget = Agent.getTime();

            Enemy teamTarget = ClosestTargetToTeam();
            Enemy agentTarget = ClosestEnemyToAgent();

            double disToCurrentTarg = Agent.getPos().distance(Agent.target.getPos());
            double disToAgentTarg = Agent.getPos().distance(agentTarget.getPos());
            double disToTeamTarg = Agent.getPos().distance(teamTarget.getPos());

            if (disToCurrentTarg > (disToTeamTarg / 2) || !NonTeamTargetPursuit) {
                //reEval
                if (disToAgentTarg < (disToTeamTarg / 2)) {
                    Agent.target = agentTarget;
                    NonTeamTargetPursuit = true;
                } else {
                    Agent.target = teamTarget;
                    NonTeamTargetPursuit = false;

                }
            }
        }
    }

    /**
     * Finds the Closest TeamMate to the Agent
     * @return Robots
     */
    public Robots ClosestTeammateToAgent() {
        Robots ClosestTarget = new Robots();
        double LastDistance = Agent.getBattleFieldHeight() * Agent.getBattleFieldWidth();
        for (TeamMate tm : Agent.teamates.values()) {
            if (tm.getAlive()) {
                double distance = Agent.getPos().distance(tm.getPos());
                if (distance < LastDistance) {
                    LastDistance = distance;
                    ClosestTarget = tm;
                }
            }
        }
        return ClosestTarget;
    }

    /**
     * Finds the Closest Enemy to the Agent
     * @return Robots
     */
    public Enemy ClosestEnemyToAgent() {
        Enemy ClosestTarget = new Enemy();
        double LastDistance = Agent.getBattleFieldHeight() * Agent.getBattleFieldWidth();
        for (Enemy en : Agent.enemies.values()) {
            if (en.getAlive()) {
                double distance = Agent.getPos().distance(en.getPos());
                if (distance < LastDistance) {
                    LastDistance = distance;
                    ClosestTarget = en;
                }
            }
        }
        return ClosestTarget;
    }

    /**
     * Finds the Closest Robot to the Agent
     * @return Robots
     */
    public Robots ClosestBotToAgent() {
        Robots ClosestTeammate = ClosestTeammateToAgent();
        Robots ClosestEnemy = ClosestEnemyToAgent();
        if (Agent.getPos().distance(ClosestTeammate.getPos()) < Agent.getPos().distance(ClosestEnemy.getPos())) {
            return ClosestTeammate;
        } else {
            return ClosestEnemy;
        }
    }

    /**
     * Finds the Closest Friendly Fire Bullet to the Agent
     * @return Virtual Bullet
     */
    public VirtualBullets ClosestBulletToAgent() {
        double LastDistance = Agent.getBattleFieldHeight() * Agent.getBattleFieldWidth();
        VirtualBullets ClosestBullet = new VirtualBullets();
        for (VirtualBullets Vb : Agent.friendlyFire.values()) {
            Long time = (Agent.getTime() - 1L);
            for (int i = 0; i
                    < 3; i++) {
                time += 1L;
                Point2D.Double BulletPos = Vb.getBulletPosition(time);
                double distance = Agent.getPos().distance(BulletPos);
                if (distance < LastDistance) {
                    LastDistance = distance;
                    ClosestBullet = Vb;
                }
            }
        }
        return ClosestBullet;
    }

    /**
     * Finds the Closest Object to the Agent
     * @return Distance to closest Object
     */
    public double ClosestObjectToAgent() {
        if (LastTime_ClosestObjectToAgent < Agent.getTime()) {
            LastTime_ClosestObjectToAgent = Agent.getTime();

            Robots ClosestBot = ClosestBotToAgent();
            VirtualBullets ClosestBullet = ClosestBulletToAgent();
            double bulletDistance = Agent.getPos().distance(ClosestBullet.getBulletPosition(Agent.getTime()));
            double botDistance = Agent.getPos().distance(ClosestBot.getPos());
            if (bulletDistance < botDistance) {
                closestObjectToAgent = bulletDistance;
                return bulletDistance;
            } else {
                closestObjectToAgent = botDistance;
                return botDistance;
            }
        }
        return closestObjectToAgent;
    }

    /**
     * Determines if the Agent is the closest member of the team to the Target
     * @return True is the Agent is the closest else it returns False
     */
    public boolean amIClosestToTarget() {
        if (LastTime_amIClosestToTarget < Agent.getTime()) {
            LastTime_amIClosestToTarget = Agent.getTime();

            double mydistance = Agent.getPos().distance(Agent.target.getPos());
            for (TeamMate tm : Agent.teamates.values()) {
                if (tm.getAlive() == true) {
                    if (mydistance > tm.getPos().distance(Agent.target.getPos())) {
                        amIClosestToTarget = false;
                        return false;
                    }
                }
            }
            amIClosestToTarget = true;
            return true;
        }
        return amIClosestToTarget;
    }
}
